DynamoDBテーブルのSet型でタグ的な項目を管理する
IoT機器を管理する方法のひとつとして、タグを検討しています。たとえば下記です。
- 機器1: 工場A、1階、担当X
- 機器2: 工場A、1階、担当Z
- 機器3: 工場B、2階、担当Z、机の上
ユーザが任意にタグを付けて、自由に管理できるイメージです。
これらの管理をDynamoDBテーブルで実現する方法として、DynamoDBのSet型を試してみました。
おすすめの方
- DynamoDBでSet型を使いたい方
ToDoテーブルのサンプル
userId | todoId | title | tags |
---|---|---|---|
u0001 | t0001 | 電池を買う | 買い物, Amazon, スーパー |
u0001 | t0002 | 映画を見る | 映画, Amazon |
u0001 | t0003 | ちくわを買う | 買い物, スーパー |
u0001 | t0004 | AWS認定試験を申し込む | 勉強, AWS, 趣味 |
u1111 | t0005 | 熊の置物を掘る | 趣味, プレゼント |
u1111 | t0006 | ラーメンを食べる | 趣味, ご飯 |
DynamoDBテーブルをデプロイする
CloudFormationテンプレート
AWSTemplateFormatVersion: "2010-09-09" Description: Todo DynamoDB Sample Resources: TodoTable: Type: AWS::DynamoDB::Table Properties: TableName: todo-tag-sample-table BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: userId AttributeType: S - AttributeName: todoId AttributeType: S KeySchema: - AttributeName: userId KeyType: HASH - AttributeName: todoId KeyType: RANGE
デプロイ
aws cloudformation deploy \ --template-file dynamodb.yaml \ --stack-name Todo-Tag-DynamoDB-Sample-stack
DynamoDBテーブルを操作する
データ追加
下記でデータを追加します。tags
部分はset型です。
import boto3 INSERT_ITEMS = [ { 'userId': 'u0001', 'todoId': 't0001', 'title': '電池を買う', 'tags': {'買い物', 'Amazon', 'スーパー'} }, { 'userId': 'u0001', 'todoId': 't0002', 'title': '映画を見る', 'tags': {'映画', 'Amazon'} }, { 'userId': 'u0001', 'todoId': 't0003', 'title': 'ちくわを買う', 'tags': {'買い物', 'スーパー'} }, { 'userId': 'u0001', 'todoId': 't0004', 'title': 'AWS認定試験を申し込む', 'tags': {'勉強', 'AWS', '趣味'} }, { 'userId': 'u1111', 'todoId': 't0005', 'title': '熊の置物を掘る', 'tags': {'趣味', 'プレゼント'} }, { 'userId': 'u1111', 'todoId': 't0006', 'title': 'ラーメンを食べる', 'tags': {'趣味', 'ご飯'} }, ] def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('todo-tag-sample-table') insert_items(table) def insert_items(table): for item in INSERT_ITEMS: table.put_item(Item=item) if __name__ == '__main__': main()
DynamoDBテーブルの様子です。tags
はStringSet
になっています。DynamoDBのドキュメントではSS
と表記される場合もあります。
データ検索
特定ユーザで、全データを取得する
userId
を指定してquery()
を実行します。
import boto3 from boto3.dynamodb.conditions import Key def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('todo-tag-sample-table') ret = get_all(table, 'u0001') dump(ret) def get_all(table, user_id): options = { 'KeyConditionExpression': Key('userId').eq(user_id), } data = [] while True: res = table.query(**options) data += res.get('Items', []) if 'LastEvaluatedKey' not in res: break options['ExclusiveStartKey'] = res['LastEvaluatedKey'] return data def dump(items): for item in items: print(item) if __name__ == '__main__': main()
$ python query_user_all.py {'todoId': 't0001', 'userId': 'u0001', 'tags': {'Amazon', '買い物', 'スーパー'}, 'title': '電池を買う'} {'todoId': 't0002', 'userId': 'u0001', 'tags': {'Amazon', '映画'}, 'title': '映画を見る'} {'todoId': 't0003', 'userId': 'u0001', 'tags': {'買い物', 'スーパー'}, 'title': 'ちくわを買う'} {'todoId': 't0004', 'userId': 'u0001', 'tags': {'趣味', '勉強', 'AWS'}, 'title': 'AWS認定試験を申し込む'}
特定ユーザで、特定タグを含むデータを取得する
userId
とtags
を指定してquery()
を実行します。
import boto3 from boto3.dynamodb.conditions import Attr, Key def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('todo-tag-sample-table') ret = get_include_tag(table, 'u0001', 'スーパー') dump(ret) def get_include_tag(table, user_id, tag): options = { 'KeyConditionExpression': Key('userId').eq(user_id), 'FilterExpression': Attr('tags').contains(tag), } data = [] while True: res = table.query(**options) data += res.get('Items', []) if 'LastEvaluatedKey' not in res: break options['ExclusiveStartKey'] = res['LastEvaluatedKey'] return data def dump(items): for item in items: print(item) if __name__ == '__main__': main()
$ python query_user_tags.py {'todoId': 't0001', 'userId': 'u0001', 'tags': {'買い物', 'スーパー', 'Amazon'}, 'title': '電池を買う'} {'todoId': 't0003', 'userId': 'u0001', 'tags': {'スーパー', '買い物'}, 'title': 'ちくわを買う'}
また、下記のように複数条件を指定可能です。
options = { 'KeyConditionExpression': Key('userId').eq(user_id), 'FilterExpression': Attr('tags').contains('買い物') & Attr('tags').contains('Amazon'), }
options = { 'KeyConditionExpression': Key('userId').eq(user_id), 'FilterExpression': Attr('tags').contains('買い物') | Attr('tags').contains('Amazon'), }
データ更新
タグを追加する
add
の更新式でタグを追加します。
import boto3 from boto3.dynamodb.conditions import Attr def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('todo-tag-sample-table') update(table, 'u0001', 't0001', '単三') def update(table, user_id, todo_id, tag): options = { 'Key': { 'userId': user_id, 'todoId': todo_id, }, 'UpdateExpression': 'add #tags :tag', 'ExpressionAttributeNames': { '#tags': 'tags', }, 'ExpressionAttributeValues': { ':tag': {tag} }, } table.update_item(**options) if __name__ == '__main__': main()
「単三」が追加されました。
タグを削除する
delete
の更新式でタグを削除します。
import boto3 from boto3.dynamodb.conditions import Attr def main(): dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('todo-tag-sample-table') update(table, 'u0001', 't0001', 'スーパー') def update(table, user_id, todo_id, tag): options = { 'Key': { 'userId': user_id, 'todoId': todo_id, }, 'UpdateExpression': 'delete #tags :tag', 'ExpressionAttributeNames': { '#tags': 'tags', }, 'ExpressionAttributeValues': { ':tag': {tag} }, } table.update_item(**options) if __name__ == '__main__': main()
「スーパー」が削除されました。
さいごに
DynamoDBのSet型を試してみました。いい感じに使えそうです。